namespace Angelus.Oracle.Entity.Common.Entity
{
    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Data.Common.CommandTrees;
    using System.Data.Metadata.Edm;
    using System.Diagnostics;
    using System.Diagnostics.Contracts;
    using System.Globalization;
    using System.Text;

    /// <summary>
    /// Description of QueryGenerator.
    /// </summary>
    internal abstract class SqlGenerator : DbExpressionVisitor<ISqlFragment>
    {
        #region Nested type: CanonicalFunctor

        /// <summary>
        /// 
        /// </summary>
        /// <param name="generator"></param>
        /// <param name="expression"></param>
        /// <returns></returns>
        protected delegate ISqlFragment CanonicalFunctor(SqlGenerator generator, DbFunctionExpression expression);

        #endregion

        #region Fields

        /// <summary>
        /// 
        /// </summary>
        protected static Dictionary<string, string> _canonicalFunctors = new Dictionary<string, string>();

        /// <summary>
        /// 
        /// </summary>
        protected static Dictionary<string, CanonicalFunctor> _functors = new Dictionary<string, CanonicalFunctor>();

        private bool _isVarRefSingle;

        /// <summary>
        /// Nested joins and extents need to know whether they should create
        /// a new Select statement, or reuse the parent's. This flag indicates
        /// whether the parent is a join or not.
        /// </summary>
        private Stack<bool> _joins;

        /// <summary>
        /// Every relational node has to pass its SELECT statement to its children
        /// This allows them (DbVariableReferenceExpression eventually) to update the list of
        /// outer extents (free variables) used by this select statement.
        /// </summary>
        private Stack<SqlSelectStatement> _statements;

        /// <summary>
        /// 
        /// </summary>
        private SymbolTable _symbols;

        #endregion

        #region Properties

        /// <summary>
        /// The top of the statement stack.
        /// </summary>
        private SqlSelectStatement CurrentStatement
        {
            get
            {
                // There is always something on the stack, so we can always Peek.
                return this._statements.Peek();
            }
        }

        /// <summary>
        /// The top of the joins stack.
        /// </summary>
        private bool IsParentAJoin
        {
            get
            {
                // There might be no entry on the stack if a Join node has never
                // been seen, so we return false in that case.
                return (0 == this._joins.Count) ? false : this._joins.Peek();
            }
        }

        /// <summary>
        /// For each column name,  we store the last integer suffix that was
        /// added to produce a unique column name. This speeds up the creation of
        /// the next unique name for this column name.
        /// </summary>
        public Dictionary<string, int> AllColumnNames { get; private set; }

        /// <summary>
        /// 
        /// </summary>
        public Dictionary<string, int> AllExtentNames { get; private set; }

        #endregion

        #region DbExpressionVisitor Overrides

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public override ISqlFragment Visit(DbAndExpression expression)
        {
            Contract.Requires(null != expression);

            return this.VisitBinaryExpression(" AND ", expression.Left, expression.Right);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public override ISqlFragment Visit(DbApplyExpression expression)
        {
            Contract.Requires<ArgumentNullException>(null != expression);

            List<DbExpressionBinding> bindings = new List<DbExpressionBinding> { expression.Input, expression.Apply };

            string joinString;
            switch (expression.ExpressionKind)
            {
                case DbExpressionKind.CrossApply:
                    joinString = "CROSS APPLY";
                    break;

                case DbExpressionKind.OuterApply:
                    joinString = "OUTER APPLY";
                    break;

                default:
                    Contract.Assert(false);
                    throw new InvalidOperationException();
            }

            // The join condition does not exist in this case, so we use null.
            // We do not have a on clause, se we use JoinType.CrossJoin
            return this.VisitJoinExpression(bindings, DbExpressionKind.CrossJoin, joinString, null);
        }

        /// <summary>
        /// For binary expressions, we delegate to <see cref="VisitBinaryExpression" />.
        /// We handle the other expression directly.
        /// </summary>
        /// <param name="expression"></param>
        /// <returns>A <see cref="SqlBuilder" />.</returns>
        public override ISqlFragment Visit(DbArithmeticExpression expression)
        {
            Contract.Requires(null != expression);

            SqlBuilder builder;

            switch (expression.ExpressionKind)
            {
                case DbExpressionKind.Divide:
                    builder = this.VisitBinaryExpression(" / ", expression.Arguments[0], expression.Arguments[1]);
                    break;

                case DbExpressionKind.Minus:
                    builder = this.VisitBinaryExpression(" - ", expression.Arguments[0], expression.Arguments[1]);
                    break;

                case DbExpressionKind.Modulo:
                    builder = this.VisitBinaryExpression(" % ", expression.Arguments[0], expression.Arguments[1]);
                    break;

                case DbExpressionKind.Multiply:
                    builder = this.VisitBinaryExpression(" * ", expression.Arguments[0], expression.Arguments[1]);
                    break;

                case DbExpressionKind.Plus:
                    builder = this.VisitBinaryExpression(" + ", expression.Arguments[0], expression.Arguments[1]);
                    break;

                case DbExpressionKind.UnaryMinus:
                    builder = new SqlBuilder();
                    builder.Append(" -(");
                    builder.Append(expression.Arguments[0].Accept(this));
                    builder.Append(")");
                    break;

                default:
                    Contract.Assert(false);
                    throw new InvalidOperationException();
            }

            return builder;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public override ISqlFragment Visit(DbCaseExpression expression)
        {
            Contract.Requires(null != expression);
            Contract.Assert(expression.When.Count == expression.Then.Count);

            SqlBuilder builder = new SqlBuilder();

            builder.Append("CASE");
            for (int index = 0, max = expression.When.Count; index < max; index++)
            {
                builder.Append(" WHEN (");
                builder.Append(expression.When[index].Accept(this));
                builder.Append(") THEN ");
                builder.Append(expression.Then[index].Accept(this));
            }

            if ((null != expression.Else) && (!(expression.Else is DbNullExpression)))
            {
                builder.Append(" ELSE ");
                builder.Append(expression.Else.Accept(this));
            }

            builder.Append(" END");
            return builder;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public override ISqlFragment Visit(DbCastExpression expression)
        {
            Contract.Requires(null != expression);

            SqlBuilder builder = new SqlBuilder();
            this.VisitCastExpression(builder, expression.Argument.Accept(this), expression.ResultType);
            return builder;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public override ISqlFragment Visit(DbComparisonExpression expression)
        {
            Contract.Requires(null != expression);

            switch (expression.ExpressionKind)
            {
                case DbExpressionKind.GreaterThan:
                    return this.VisitBinaryExpression(" > ", expression.Left, expression.Right);

                case DbExpressionKind.GreaterThanOrEquals:
                    return this.VisitBinaryExpression(" >= ", expression.Left, expression.Right);

                case DbExpressionKind.Equals:
                    return this.VisitBinaryExpression(" = ", expression.Left, expression.Right);

                case DbExpressionKind.LessThan:
                    return this.VisitBinaryExpression(" < ", expression.Left, expression.Right);

                case DbExpressionKind.LessThanOrEquals:
                    return this.VisitBinaryExpression(" <= ", expression.Left, expression.Right);

                case DbExpressionKind.NotEquals:
                    return this.VisitBinaryExpression(" <> ", expression.Left, expression.Right);

                default:
                    Contract.Assert(false); // The constructor should have prevented this
                    throw new InvalidOperationException();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public override ISqlFragment Visit(DbConstantExpression expression)
        {
            Contract.Requires(null != expression);

            SqlBuilder builder = new SqlBuilder();

            PrimitiveTypeKind typeKind;
            if (MetadataHelpers.TryGetPrimitiveTypeKind(expression.ResultType, out typeKind))
            {
                switch (typeKind)
                {
                    case PrimitiveTypeKind.Binary:
                        throw new NotSupportedException("Binary type is not supported in the constraints.");

                    case PrimitiveTypeKind.Boolean:
                        builder.Append((bool) expression.Value ? "1" : "0");
                        break;

                    case PrimitiveTypeKind.Byte:
                    case PrimitiveTypeKind.SByte:
                        builder.Append(expression.Value);
                        break;

                    case PrimitiveTypeKind.DateTime:
                        builder.Append("CAST(");
                        DateTime time = (DateTime)expression.Value;
                        builder.Append(this.EscapeSingleQuote(time.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture), false /* IsUnicode */));
                        builder.Append(" AS ");
                        builder.Append(expression.ResultType.EdmType.Name);
                        builder.Append(")");
                        break;

                    case PrimitiveTypeKind.Decimal:
                        string value = ((decimal) expression.Value).ToString(CultureInfo.InvariantCulture);
                        builder.Append(value);
                        break;

                    case PrimitiveTypeKind.Double:
                        builder.Append(((double) expression.Value).ToString(CultureInfo.InvariantCulture));
                        break;

                    case PrimitiveTypeKind.Guid:
                        builder.Append(this.EscapeSingleQuote(((Guid) expression.Value).ToString(), false /* IsUnicode */));
                        break;

                    case PrimitiveTypeKind.Single:
                        builder.Append(((float) expression.Value).ToString(CultureInfo.InvariantCulture));
                        break;

                    case PrimitiveTypeKind.Int16:
                    case PrimitiveTypeKind.Int32:
                    case PrimitiveTypeKind.Int64:
                        builder.Append(expression.Value.ToString());
                        break;

                    case PrimitiveTypeKind.String:
                        bool isUnicode;
                        if (!MetadataHelpers.TryGetIsUnicode(expression.ResultType, out isUnicode))
                        {
                            isUnicode = true;
                        }

                        builder.Append(this.EscapeSingleQuote(expression.Value as string, isUnicode));
                        break;

                    default:
                        throw new NotSupportedException();
                }
            }

            return builder;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public override ISqlFragment Visit(DbCrossJoinExpression expression)
        {
            Contract.Requires(null != expression);

            return this.VisitJoinExpression(expression.Inputs, expression.ExpressionKind, "CROSS JOIN", null);
        }

        /// <summary>
        /// <see cref="DbIsOfExpression"/> is illegal at this stage.
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public override ISqlFragment Visit(DbDerefExpression expression)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public override ISqlFragment Visit(DbDistinctExpression expression)
        {
            Contract.Requires(null != expression);

            SqlSelectStatement statement = this.VisitExpressionEnsureSqlStatement(expression.Argument);

            if (!IsCompatible(statement, expression.ExpressionKind))
            {
                Symbol symbol;
                TypeUsage usage = MetadataHelpers.GetElementTypeUsage(expression.Argument.ResultType);
                statement = this.CreateNewSelectStatement(statement, "DISTINCT", usage, out symbol);
                this.AddFromSymbol(statement, "DISTINCT", symbol, false);
            }

            statement.IsDistinct = true;
            return statement;
        }

        /// <summary>
        /// An element expression returns a scalar - so it is translated to
        /// ( Select ... )
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public override ISqlFragment Visit(DbElementExpression expression)
        {
            Contract.Requires(null != expression);

            SqlBuilder builder = new SqlBuilder();

            builder.Append("(");
            builder.Append(this.VisitExpressionEnsureSqlStatement(expression.Argument));
            builder.Append(")");

            return builder;
        }

        /// <summary>
        /// <see cref="DbEntityRefExpression"/> is illegal at this stage.
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public override ISqlFragment Visit(DbEntityRefExpression expression)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// <see cref="DbEntityRefExpression"/> is illegal at this stage.
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public override ISqlFragment Visit(DbExpression expression)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// <see cref="DbRefKeyExpression"/> is illegal at this stage.
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public override ISqlFragment Visit(DbExceptExpression expression)
        {
            Contract.Requires(null != expression);

            return this.VisitSetOpExpression(expression.Left, expression.Right, "EXCEPT");
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public override ISqlFragment Visit(DbFilterExpression expression)
        {
            Contract.Requires(null != expression);

            return this.VisitFilterExpression(expression.Input, expression.Predicate, false);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public override ISqlFragment Visit(DbFunctionExpression expression)
        {
            Contract.Requires(null != expression);

            if (IsSpecialCanonicalFunction(expression))
            {
                return this.HandleCanonicalFunctions(expression);
            }

            return this.HandleDefaultFunctions(expression);
        }

        /// <summary>
        /// <see cref="Visit(DbFilterExpression)"/> for general details.
        /// We modify both the GroupBy and the Select fields of the OracleSelectStatement.
        /// GroupBy gets just the keys without aliases,
        /// and Select gets the keys and the aggregates with aliases.
        /// 
        /// Whenever there exists at least one aggregate with an argiyment that is not a simple
        /// <see cref="DbPropertyExpression"/>  over <see cref="DbVariableReferenceExpression"/>,
        /// we create a nested query in which we alias the arguments to the aggregates.
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public override ISqlFragment Visit(DbGroupByExpression expression)
        {
            Contract.Requires(null != expression);

            Symbol symbol;
            SqlSelectStatement statement = this.VisitInputExpression(expression.Input.Expression,
                                                                     expression.Input.VariableName,
                                                                     expression.Input.VariableType, out symbol);

            // GroupBy is compatible with Filter and OrderBy
            // but not with Project, GroupBy
            if (!IsCompatible(statement, expression.ExpressionKind))
            {
                statement = this.CreateNewSelectStatement(statement, expression.Input.VariableName,
                                                          expression.Input.VariableType, out symbol);
            }

            this._statements.Push(statement);
            this._symbols.EnterScope();

            this.AddFromSymbol(statement, expression.Input.VariableName, symbol);
            // This line is not present for other relation nodes.
            this._symbols.Add(expression.Input.GroupVariableName, symbol);

            // The enumerator is shared by both the keys and the aggregates,
            // so, we dot not close it in between
            RowType rowType =
                MetadataHelpers.GetEdmType<RowType>(
                    MetadataHelpers.GetEdmType<CollectionType>(expression.ResultType).TypeUsage);

            // Whenever there exists at least one aggregate with an argument that is not simply a PropertyExpression
            // over a VarRefExpression, we need a nested query in which we alias the arguments to the aggregates.
            bool needsInnerQuery = NeedsInnerQuery(expression.Aggregates);

            SqlSelectStatement result;
            if (needsInnerQuery)
            {
                result = this.CreateNewSelectStatement(statement, expression.Input.VariableName,
                                                       expression.Input.VariableType, false, out symbol);
                this.AddFromSymbol(result, expression.Input.VariableName, symbol, false);
            }
            else
            {
                result = statement;
            }

            using (IEnumerator<EdmProperty> members = rowType.Properties.GetEnumerator())
            {
                members.MoveNext();
                Contract.Assert(result.Select.IsEmpty);

                string separator = string.Empty;
                foreach (DbExpression key in expression.Keys)
                {
                    EdmProperty member = members.Current;
                    string alias = this.QuoteIdentifier(member.Name);

                    result.GroupBy.Append(separator);

                    ISqlFragment keySql = key.Accept(this);

                    if (!needsInnerQuery)
                    {
                        // Default translation : Key AS Alias
                        result.Select.Append(separator);
                        result.Select.AppendLine();
                        result.Select.Append(keySql);
                        result.Select.Append(" AS ");
                        result.Select.Append(alias);

                        result.GroupBy.Append(keySql);
                    }
                    else
                    {
                        // The inner query contains the default translation Key AS Alias
                        statement.Select.Append(separator);
                        statement.Select.AppendLine();
                        statement.Select.Append(keySql);
                        statement.Select.Append(" AS ");
                        statement.Select.Append(alias);

                        // The outer resulting query projects over the key aliased in the inner query:
                        // symbol.Alias AS Alias
                        result.Select.Append(separator);
                        result.Select.AppendLine();
                        result.Select.Append(symbol);
                        result.Select.Append(".");
                        result.Select.Append(alias);
                        result.Select.Append(" AS  ");
                        result.Select.Append(alias);

                        result.GroupBy.Append(alias);
                    }

                    separator = ", ";
                    members.MoveNext();
                }

                foreach (DbAggregate aggregate in expression.Aggregates)
                {
                    EdmProperty property = members.Current;
                    string alias = this.QuoteIdentifier(property.Name);

                    Contract.Assert(1 == aggregate.Arguments.Count);
                    ISqlFragment fragment = aggregate.Arguments[0].Accept(this);

                    object argument;
                    if (needsInnerQuery)
                    {
                        // In this case the argument to the aggrgate is reference to the one projected out by the
                        // inner query
                        SqlBuilder builder = new SqlBuilder();
                        builder.Append(symbol);
                        builder.Append(".");
                        builder.Append(alias);

                        argument = builder;

                        statement.Select.Append(separator);
                        statement.Select.AppendLine();
                        statement.Select.Append(fragment);
                        statement.Select.Append(" AS ");
                        statement.Select.Append(alias);
                    }
                    else
                    {
                        argument = fragment;
                    }

                    ISqlFragment query = this.VisitAggregate(aggregate, argument);

                    result.Select.Append(separator);
                    result.Select.AppendLine();
                    result.Select.Append(query);
                    result.Select.Append(" AS ");
                    result.Select.Append(alias);

                    separator = ", ";
                    members.MoveNext();
                }
            }

            this._symbols.ExitScope();
            this._statements.Pop();

            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public override ISqlFragment Visit(DbIntersectExpression expression)
        {
            Contract.Requires(null != expression);

            return this.VisitSetOpExpression(expression.Left, expression.Right, "INTERSECT");
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public override ISqlFragment Visit(DbIsEmptyExpression expression)
        {
            Contract.Requires(null != expression);

            return this.VisitIsEmptyExpression(expression, false);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public override ISqlFragment Visit(DbIsNullExpression expression)
        {
            Contract.Requires(null != expression);

            return this.VisitIsNullExpression(expression, false);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public override ISqlFragment Visit(DbIsOfExpression expression)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public override ISqlFragment Visit(DbJoinExpression expression)
        {
            Contract.Requires(null != expression);
            
            string joinType;
            switch (expression.ExpressionKind)
            {
                case DbExpressionKind.FullOuterJoin:
                    joinType = "FULL OUTER JOIN";
                    break;

                case DbExpressionKind.InnerJoin:
                    joinType = "INNER JOIN";
                    break;

                case DbExpressionKind.LeftOuterJoin:
                    joinType = "LEFT OUTER JOIN";
                    break;

                default:
                    Contract.Assert(false);
                    joinType = null;
                    break;
            }

            List<DbExpressionBinding> bindings = new List<DbExpressionBinding>(2) { expression.Left, expression.Right };
            return this.VisitJoinExpression(bindings, expression.ExpressionKind, joinType, expression.JoinCondition);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public override ISqlFragment Visit(DbLikeExpression expression)
        {
            Contract.Requires(null != expression);

            SqlBuilder builder = new SqlBuilder();
            builder.Append(expression.Argument.Accept(this));
            builder.Append(" LIKE ");
            builder.Append(expression.Pattern.Accept(this));

            // if the ESCAPE expression is a DbNullExpression, then that's tantamount to
            // not having an ESCAPE at all
            if (DbExpressionKind.Null != expression.Escape.ExpressionKind)
            {
                builder.Append(" ESCAPE ");
                builder.Append(expression.Escape.Accept(this));
            }

            return builder;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public override ISqlFragment Visit(DbNotExpression expression)
        {
            Contract.Requires(null != expression);

            DbNotExpression notExpression = expression.Argument as DbNotExpression;
            if (null != notExpression)
            {
                return notExpression.Argument.Accept(this);
            }

            DbIsEmptyExpression isEmptyExpression = expression.Argument as DbIsEmptyExpression;
            if (null != isEmptyExpression)
            {
                return this.VisitIsEmptyExpression(isEmptyExpression, true);
            }

            DbIsNullExpression isNullExpression = expression.Argument as DbIsNullExpression;
            if (null != isNullExpression)
            {
                return this.VisitIsNullExpression(isNullExpression, true);
            }

            DbComparisonExpression comparisonExpression = expression.Argument as DbComparisonExpression;
            if (null != comparisonExpression)
            {
                return this.VisitBinaryExpression(" <> ", comparisonExpression.Left, comparisonExpression.Right);
            }

            SqlBuilder builder = new SqlBuilder();
            builder.Append(" NOT (");
            builder.Append(expression.Argument.Accept(this));
            builder.Append(")");

            return builder;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public override ISqlFragment Visit(DbNullExpression expression)
        {
            Contract.Requires(null != expression);

            SqlBuilder builder = new SqlBuilder();

            // always cast nulls
            /*builder.Append("CAST(NULL AS ");
			TypeUsage usage = expression.ResultType;
			builder.Append(GetOraclePrimitiveType(usage));
			builder.Append(")");*/
            builder.Append("NULL");

            return builder;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public override ISqlFragment Visit(DbOfTypeExpression expression)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public override ISqlFragment Visit(DbOrExpression expression)
        {
            Contract.Requires(null != expression);

            return this.VisitBinaryExpression(" OR ", expression.Left, expression.Right);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public override ISqlFragment Visit(DbParameterReferenceExpression expression)
        {
            Contract.Requires(null != expression);

            SqlBuilder builder = new SqlBuilder();
            builder.Append(":" + expression.ParameterName);

            return builder;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public override ISqlFragment Visit(DbProjectExpression expression)
        {
            Contract.Requires(null != expression);

            Symbol symbol;
            SqlSelectStatement statement = this.VisitInputExpression(expression.Input.Expression,
                                                                     expression.Input.VariableName,
                                                                     expression.Input.VariableType, out symbol);

            // Project is compatible with Filter
            // but not with Project, GroupBy
            if (!IsCompatible(statement, expression.ExpressionKind))
            {
                statement = this.CreateNewSelectStatement(statement, expression.Input.VariableName,
                                                          expression.Input.VariableType, out symbol);
            }

            this._statements.Push(statement);
            this._symbols.EnterScope();

            this.AddFromSymbol(statement, expression.Input.VariableName, symbol);

            // Project is the only node that can have DbNewInstanceExpression as a child
            // so we have to check it here.
            // We call VisitNewInstanceExpression instead of visit(DbNewInstanceExpression), since
            // the latter throws.
            DbNewInstanceExpression newInstanceExpression = expression.Projection as DbNewInstanceExpression;
            if (null != newInstanceExpression)
            {
                statement.Select.Append(this.VisitNewInstanceExpression(newInstanceExpression));
            }
            else
            {
                statement.Select.Append(expression.Projection.Accept(this));
            }

            this._symbols.ExitScope();
            this._statements.Pop();

            return statement;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public override ISqlFragment Visit(DbPropertyExpression expression)
        {
            Contract.Requires(null != expression);

            ISqlFragment fragment = expression.Instance.Accept(this);

            // since the DbVariableReferenceExpression is a proper child of ours, we can reset
            // isVarSingle
            DbVariableReferenceExpression variableRefExpression = expression.Instance as DbVariableReferenceExpression;
            if (null != variableRefExpression)
            {
                this._isVarRefSingle = false;
            }

            // we need to flatten, and have not yet seen the first nested SELECT statement.
            JoinSymbol joinSymbol = fragment as JoinSymbol;
            if (null != joinSymbol)
            {
                Contract.Assert(joinSymbol.NameToExtent.ContainsKey(expression.Property.Name));
                if (joinSymbol.IsNestedJoin)
                {
                    return new SymbolPair(joinSymbol, joinSymbol.NameToExtent[expression.Property.Name]);
                }

                return joinSymbol.NameToExtent[expression.Property.Name];
            }

            SymbolPair pair = fragment as SymbolPair;
            if (null != pair)
            {
                JoinSymbol columnJoinSymbol = pair.Column as JoinSymbol;
                if (null != columnJoinSymbol)
                {
                    pair.Column = columnJoinSymbol.NameToExtent[expression.Property.Name];
                    return pair;
                }

                // symbolPair.Column has the base extent.
                // we need the symbol for the column, since it might have been renamed
                // when handling a JOIN
                if (pair.Column.Columns.ContainsKey(expression.Property.Name))
                {
                    SqlBuilder builder = new SqlBuilder();
                    builder.Append(pair.Source);
                    builder.Append(".");
                    builder.Append(pair.Column.Columns[expression.Property.Name]);
                    
                    return builder;
                }
            }

            SqlBuilder newer = new SqlBuilder();
            newer.Append(fragment);
            newer.Append(".");

            // At this point the column name cannot be renamed, so we do
            // not use a symbol.
            newer.Append(this.QuoteIdentifier(expression.Property.Name));

            return newer;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public override ISqlFragment Visit(DbQuantifierExpression expression)
        {
            Contract.Requires(null != expression);

            SqlBuilder builder = new SqlBuilder();

            bool negatePredicate = (DbExpressionKind.All == expression.ExpressionKind);
            if (DbExpressionKind.Any == expression.ExpressionKind)
            {
                builder.Append("EXISTS (");
            }
            else
            {
                Contract.Assert(DbExpressionKind.All == expression.ExpressionKind);
                builder.Append("NOT EXISTS (");
            }

            SqlSelectStatement statement = this.VisitFilterExpression(expression.Input, expression.Predicate,
                                                                      negatePredicate);
            if (statement.Select.IsEmpty)
            {
                this.AddDefaultColumns(statement);
            }

            builder.Append(statement);
            builder.Append(")");

            return builder;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public override ISqlFragment Visit(DbRefExpression expression)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public override ISqlFragment Visit(DbRefKeyExpression expression)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public override ISqlFragment Visit(DbRelationshipNavigationExpression expression)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public override ISqlFragment Visit(DbSkipExpression expression)
        {
            Contract.Requires(null != expression);
            Contract.Assert(expression.Count is DbConstantExpression || expression.Count is DbParameterReferenceExpression,
                "DbSkipExpression.Count is of invalid expression type");

            // Visit the input
            Symbol symbol;
            SqlSelectStatement statement = this.VisitInputExpression(expression.Input.Expression,
                                                                     expression.Input.VariableName,
                                                                     expression.Input.VariableType, out symbol);

            // Skip is not compatible with anything that OrderBy is not compatible with, as well as with distinct
            if (!IsCompatible(statement, expression.ExpressionKind))
            {
                statement = this.CreateNewSelectStatement(statement, expression.Input.VariableName,
                                                          expression.Input.VariableType, out symbol);
            }

            this._statements.Push(statement);
            this._symbols.EnterScope();

            this.AddFromSymbol(statement, expression.Input.VariableName, symbol);

            // Add the default columns
            Contract.Assert(statement.Select.IsEmpty);
            List<Symbol> columns = this.AddDefaultColumns(statement);

            statement.Select.Append(", ROW_NUMBER() ");
            statement.Select.Append(" OVER (ORDER BY ");
            if (0 == expression.SortOrder.Count)
            {
                statement.Select.Append("ROWNUM");
            }
            else
            {
                this.AddSortKeys(statement.Select, expression.SortOrder);
            }

            statement.Select.Append(")");
            statement.Select.Append(" AS ");
            Symbol rowNumberSymbol = new Symbol("ROW_NUMBER", null);

            statement.Select.Append(rowNumberSymbol);

            // the inner statement is comple, its scopes need not be valid any longer
            this._symbols.ExitScope();
            this._statements.Pop();

            // create the resulting statement
            // see CreateNewSelectStatement, it is very simiar
            SqlSelectStatement result = new SqlSelectStatement();
            result.From.Append("( ");
            result.From.Append(statement);
            result.From.AppendLine();
            result.From.Append(") ");

            // create a symbol for the input
            Symbol resultSymbol = null;

            if (1 == statement.FromExtents.Count)
            {
                JoinSymbol joinSymbol = statement.FromExtents[0] as JoinSymbol;
                if (null != joinSymbol)
                {
                    // Note : statement.FromExtents will not do, since it might
                    // just be an alias of joinSymbol, and we want an actual OracleJoinSymbol
                    JoinSymbol newSymbol = new JoinSymbol(expression.Input.VariableName, expression.Input.VariableType,
                                                   joinSymbol.ExtentList)
                                        {
                                            IsNestedJoin = true,
                                            ColumnList = columns,
                                            FlattenedExtentList = joinSymbol.FlattenedExtentList
                                        };

                    // This indicates that the joinSylbol is a blocking scope
                    // i.e. it hides/renames extent columns

                    resultSymbol = newSymbol;
                }
            }

            if (null == resultSymbol)
            {
                // This is juste a simple extent/OracleSelectStatement,
                // and we can get the column list from the type.
                resultSymbol = new Symbol(expression.Input.VariableName, expression.Input.VariableType);
            }

            // Add the ORDER BY part
            this._statements.Push(result);
            this._symbols.EnterScope();

            this.AddFromSymbol(result, expression.Input.VariableName, resultSymbol);

            // Add the predicate
            result.Where.Append(resultSymbol);
            result.Where.Append(".");
            result.Where.Append(rowNumberSymbol);
            result.Where.Append(" > ");
            result.Where.Append(this.HandleCountExpression(expression.Count));

            this.AddSortKeys(result.OrderBy, expression.SortOrder);

            this._symbols.ExitScope();
            this._statements.Pop();

            return result;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public override ISqlFragment Visit(DbScanExpression expression)
        {
            Contract.Requires(null != expression);

            EntitySetBase target = expression.Target;

            if (this.IsParentAJoin)
            {
                SqlBuilder builder = new SqlBuilder();
                builder.Append(this.GetTargetTSql(target));
                
                return builder;
            }

            SqlSelectStatement statement = new SqlSelectStatement();
            statement.From.Append(this.GetTargetTSql(target));
            return statement;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public override ISqlFragment Visit(DbSortExpression expression)
        {
            Contract.Requires(null != expression);

            Symbol symbol;
            SqlSelectStatement statement = this.VisitInputExpression(expression.Input.Expression,
                                                                     expression.Input.VariableName,
                                                                     expression.Input.VariableType, out symbol);

            // OrderBy is compatible with Filter
            // and nothing else
            if (!IsCompatible(statement, expression.ExpressionKind))
            {
                statement = this.CreateNewSelectStatement(statement, expression.Input.VariableName,
                                                          expression.Input.VariableType, out symbol);
            }

            this._statements.Push(statement);
            this._symbols.EnterScope();

            this.AddFromSymbol(statement, expression.Input.VariableName, symbol);
            this.AddSortKeys(statement.OrderBy, expression.SortOrder);

            this._symbols.ExitScope();
            this._statements.Pop();

            return statement;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public override ISqlFragment Visit(DbTreatExpression expression)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public override ISqlFragment Visit(DbUnionAllExpression expression)
        {
            Contract.Requires(null != expression);

            return this.VisitSetOpExpression(expression.Left, expression.Right, "UNION ALL");
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public override ISqlFragment Visit(DbVariableReferenceExpression expression)
        {
            Contract.Requires(null != expression);

            if (this._isVarRefSingle)
            {
                throw new NotSupportedException();
            }

            this._isVarRefSingle = true;
            Symbol symbol = this._symbols.Lookup(expression.VariableName);
            if (!this.CurrentStatement.FromExtents.Contains(symbol))
            {
                this.CurrentStatement.OutExtents[symbol] = true;
            }

            return symbol;
        }

        #endregion

        #region Abstract Implementation

        /// <summary>
        /// Before we embed a string literal in a SQL string, we should
        /// convert all ' to '', and enclose the whole string in single quotes.
        /// </summary>
        /// <param name="value"></param>
        /// <param name="isUnicode"></param>
        /// <returns>The escaped sql string.</returns>
        protected abstract string EscapeSingleQuote(string value, bool isUnicode);

        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        internal abstract string QuoteIdentifier(string name);

        /// <summary>
        /// 
        /// </summary>
        /// <param name="builder"></param>
        /// <param name="fragment"></param>
        /// <param name="type"></param>
        protected abstract void VisitCastExpression(SqlBuilder builder, ISqlFragment fragment, TypeUsage type);

        #endregion

        #region Query Generator Implementation

        /// <summary>
        /// 
        /// </summary>
        /// <param name="tree"></param>
        /// <returns></returns>
        public string GenerateQuery(DbQueryCommandTree tree)
        {
            DbQueryCommandTree targetTree = tree;

            this._statements = new Stack<SqlSelectStatement>();
            this._joins = new Stack<bool>();

            this.AllExtentNames = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
            this.AllColumnNames = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
            this._symbols = new SymbolTable();

            // Literals will not be converted to parameters.

            ISqlFragment result;
            if (MetadataHelpers.IsCollectionType(targetTree.Query.ResultType.EdmType))
            {
                SqlSelectStatement sqlStatement = this.VisitExpressionEnsureSqlStatement(targetTree.Query);
                Contract.Assert(sqlStatement != null, "The outer most sql statment is null");
                sqlStatement.IsTopMost = true;
                result = sqlStatement;
            }
            else
            {
                SqlBuilder sqlBuilder = new SqlBuilder();
                sqlBuilder.Append("SELECT ");
                sqlBuilder.Append(targetTree.Query.Accept(this));

                result = sqlBuilder;
            }

            if (this._isVarRefSingle)
            {
                throw new NotSupportedException();
                // A DbVariableReferenceExpression has to be a child of DbPropertyExpression or MethodExpression
            }

            // Check that the parameter stacks are not leaking.
            Contract.Assert(this._statements.Count == 0);
            Contract.Assert(this._joins.Count == 0);

            return this.WriteQuery(result);
        }

        #endregion

        #region Private Implementation

        /// <summary>
        /// Convert the SQL fragments to a string.
        /// We have to setup the Stream for writing.
        /// </summary>
        /// <param name="fragment"></param>
        /// <returns>A string representing the SQL to be executed.</returns>
        private string WriteQuery(ISqlFragment fragment)
        {
            StringBuilder builder = new StringBuilder(1024);
            using (SqlWriter writer = new SqlWriter(builder))
            {
                fragment.WriteSql(writer, this);
            }

            return builder.ToString();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="statement"></param>
        /// <param name="parameterName"></param>
        /// <param name="parameterType"></param>
        /// <param name="symbol"></param>
        /// <returns></returns>
        protected SqlSelectStatement CreateNewSelectStatement(SqlSelectStatement statement, string parameterName,
                                                              TypeUsage parameterType, out Symbol symbol)
        {
            return this.CreateNewSelectStatement(statement, parameterName, parameterType, true, out symbol);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="statement"></param>
        /// <param name="parameterName"></param>
        /// <param name="parameterType"></param>
        /// <param name="finalizeStatement"></param>
        /// <param name="symbol"></param>
        /// <returns></returns>
        protected SqlSelectStatement CreateNewSelectStatement(SqlSelectStatement statement, string parameterName,
                                                              TypeUsage parameterType, bool finalizeStatement,
                                                              out Symbol symbol)
        {
            symbol = null;

            // Finalize the statement
            if (finalizeStatement && statement.Select.IsEmpty)
            {
                List<Symbol> columns = this.AddDefaultColumns(statement);

                // This could not have been called from a join node.
                Contract.Assert(1 == statement.FromExtents.Count, "This could not have been called from a join node.");

                // If the statement has a join as its input,
                // clone the join symbol, so that we "reuse" the
                // join symbol. Normally, we create a new symbol
                JoinSymbol joinSymbol = statement.FromExtents[0] as JoinSymbol;
                if (null != joinSymbol)
                {
                    // Note: statement.FromExtents will not do, since it might
                    // just be an alias of joinSymbo, and we want an actual OracleJoinSymbol.
                    JoinSymbol newer = new JoinSymbol(parameterName, parameterType, joinSymbol.ExtentList)
                                    {
                                        // This indicates that the statement is a blocking scope
                                        // i.e. it hides/renames extent columns.
                                        IsNestedJoin = true,
                                        ColumnList = columns,
                                        FlattenedExtentList = joinSymbol.FlattenedExtentList
                                    };

                    symbol = newer;
                }
            }

            if (null == symbol)
            {
                // This is just a simple extent/OracleSelectStatement
                // and we can get the column list from the type.
                symbol = new Symbol(parameterName, parameterType);
            }

            // Observe that the following looks like the body of Visit(ExtentExpression).
            SqlSelectStatement selectStatement = new SqlSelectStatement();
            selectStatement.From.Append("( ");
            selectStatement.From.Append(statement);
            selectStatement.From.AppendLine();
            selectStatement.From.Append(") ");

            return selectStatement;
        }

        /// <summary>
        /// Simply calls <see cref="VisitExpressionEnsureSqlStatement(DbExpression, bool)" />
        /// with the addDefaultColumns set to true.
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        protected SqlSelectStatement VisitExpressionEnsureSqlStatement(DbExpression expression)
        {
            return this.VisitExpressionEnsureSqlStatement(expression, true);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <param name="addDefaultColumns"></param>
        /// <returns></returns>
        protected SqlSelectStatement VisitExpressionEnsureSqlStatement(DbExpression expression, bool addDefaultColumns)
        {
            Contract.Assert(MetadataHelpers.IsCollectionType(expression.ResultType));

            SqlSelectStatement statement;
            switch (expression.ExpressionKind)
            {
                case DbExpressionKind.Sort:
                case DbExpressionKind.Project:
                case DbExpressionKind.Filter:
                case DbExpressionKind.GroupBy:
                    statement = expression.Accept(this) as SqlSelectStatement;
                    break;

                default:
                    Symbol symbol;
                    const string parameterName = "c";
                    this._symbols.EnterScope();

                    TypeUsage usage;

                    switch (expression.ExpressionKind)
                    {
                        case DbExpressionKind.Scan:
                        case DbExpressionKind.OuterApply:
                        case DbExpressionKind.CrossApply:
                        case DbExpressionKind.FullOuterJoin:
                        case DbExpressionKind.InnerJoin:
                        case DbExpressionKind.LeftOuterJoin:
                            usage = MetadataHelpers.GetElementTypeUsage(expression.ResultType);
                            break;

                        default:
                            Contract.Assert(MetadataHelpers.IsCollectionType(expression.ResultType));
                            usage = MetadataHelpers.GetEdmType<CollectionType>(expression.ResultType).TypeUsage;
                            break;
                    }

                    statement = this.VisitInputExpression(expression, parameterName, usage, out symbol);
                    this.AddFromSymbol(statement, parameterName, symbol);
                    this._symbols.ExitScope();
                    break;
            }

            if (addDefaultColumns && statement.Select.IsEmpty)
            {
                this.AddDefaultColumns(statement);
            }

            return statement;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="statement"></param>
        /// <param name="parameterName"></param>
        /// <param name="symbol"></param>
        protected void AddFromSymbol(SqlSelectStatement statement, string parameterName, Symbol symbol)
        {
            this.AddFromSymbol(statement, parameterName, symbol, true);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="statement"></param>
        /// <param name="parameterName"></param>
        /// <param name="symbol"></param>
        /// <param name="addToSymbolTable"></param>
        protected void AddFromSymbol(SqlSelectStatement statement, string parameterName, Symbol symbol,
                                     bool addToSymbolTable)
        {
            // the first check is true if this is a new statement
            // the second check is true if we are in a join - we do not
            // check if we are in a join context.
            // We do not want to add "AS alias" if it has been done already
            // e.g. when we are reusing the Sql statement.
            if ((0 == statement.FromExtents.Count) || (symbol != statement.FromExtents[0]))
            {
                statement.FromExtents.Add(symbol);
                // ORACLE - cannot use AS between table name and alias 
                statement.From.Append(" ");
                statement.From.Append(symbol);

                // We have this inside the if statement, since
                // we only want to add extents that are actually used.
                this.AllColumnNames[symbol.Name] = 0;
            }

            if (addToSymbolTable)
            {
                this._symbols.Add(parameterName, symbol);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="builder"></param>
        /// <param name="sortKeys"></param>
        private void AddSortKeys(SqlBuilder builder, IEnumerable<DbSortClause> sortKeys)
        {
            string separator = string.Empty;
            foreach (DbSortClause clause in sortKeys)
            {
                builder.Append(separator);
                builder.Append(clause.Expression.Accept(this));
                Contract.Assert(null != clause.Collation);
                if (!string.IsNullOrEmpty(clause.Collation))
                {
                    builder.Append(" COLLATE ");
                    builder.Append(clause.Collation);
                }

                builder.Append(clause.Ascending ? " ASC" : " DESC");
                separator = ", ";
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="target"></param>
        /// <returns></returns>
        private string GetTargetTSql(EntitySetBase target)
        {
            // construct escaped T-SQL referencing entity set
            StringBuilder builder = new StringBuilder(50);
            string definingQuery = MetadataHelpers.TryGetValueForMetadataProperty<string>(target, "DefiningQuery");
            if (!string.IsNullOrEmpty(definingQuery))
            {
                builder.Append("(");
                builder.Append(definingQuery);
                builder.Append(")");
            }
            else
            {
                string schemaName = MetadataHelpers.TryGetValueForMetadataProperty<string>(target, "Schema");
                if (!string.IsNullOrEmpty(schemaName))
                {
                    builder.Append(this.QuoteIdentifier(schemaName));
                    builder.Append(".");
                }
                else
                {
                    builder.Append(this.QuoteIdentifier(target.EntityContainer.Name));
                    builder.Append(".");
                }

                string tableName = MetadataHelpers.TryGetValueForMetadataProperty<string>(target, "Table");
                if (!string.IsNullOrEmpty(tableName))
                {
                    builder.Append(this.QuoteIdentifier(tableName));
                }
                else
                {
                    builder.Append(this.QuoteIdentifier(target.Name));
                }
            }

            return builder.ToString();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="left"></param>
        /// <param name="right"></param>
        /// <param name="separator"></param>
        /// <returns></returns>
        private ISqlFragment VisitSetOpExpression(DbExpression left, DbExpression right, string separator)
        {
            SqlSelectStatement leftStatement = this.VisitExpressionEnsureSqlStatement(left);
            SqlSelectStatement rightStatement = this.VisitExpressionEnsureSqlStatement(right);

            SqlBuilder builder = new SqlBuilder();

            builder.Append(leftStatement);
            builder.AppendLine();
            builder.Append(separator);
            builder.AppendLine();
            builder.Append(rightStatement);

            return builder;
        }

        /// <summary>
        /// Aggregates are not visited by the normal visitor walk.
        /// </summary>
        /// <param name="aggregate"></param>
        /// <param name="argument"></param>
        /// <returns></returns>
        private SqlBuilder VisitAggregate(DbAggregate aggregate, object argument)
        {
            SqlBuilder builder = new SqlBuilder();

            DbFunctionAggregate functionAggregate = aggregate as DbFunctionAggregate;
            if (null == functionAggregate)
            {
                throw new NotSupportedException();
            }

            throw new NotSupportedException();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="op"></param>
        /// <param name="left"></param>
        /// <param name="right"></param>
        /// <returns></returns>
        private SqlBuilder VisitBinaryExpression(string op, DbExpression left, DbExpression right)
        {
            SqlBuilder builder = new SqlBuilder();

            //bool isCaseOptional = (left.ExpressionKind == right.ExpressionKind);
            this.ParenthesizeExpression(left, builder);
            builder.Append(op);
            this.ParenthesizeExpression(right, builder);

            return builder;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        private ISqlFragment VisitNewInstanceExpression(DbNewInstanceExpression expression)
        {
            SqlBuilder builder = new SqlBuilder();

            RowType rowType = expression.ResultType.EdmType as RowType;
            if (null != rowType)
            {
                ReadOnlyMetadataCollection<EdmProperty> members = rowType.Properties;
                string separator = string.Empty;

                for (int index = 0, max = expression.Arguments.Count; index < max; ++index)
                {
                    DbExpression argument = expression.Arguments[index];
                    if (MetadataHelpers.IsRowType(argument.ResultType))
                    {
                        // we do not support nested records or other complex objects.
                        throw new NotSupportedException();
                    }

                    EdmProperty member = members[index];

                    builder.Append(separator);
                    builder.AppendLine();
                    builder.Append(argument.Accept(this));
                    builder.Append(" AS ");
                    builder.Append(this.QuoteIdentifier(member.Name));
                    separator = ", ";
                }
            }
            else
            {
                // types other then RowType (such as UDTs for instance) are not supported.
                throw new NotSupportedException();
            }

            return builder;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="statement"></param>
        /// <param name="symbol"></param>
        /// <param name="columns"></param>
        /// <param name="map"></param>
        /// <param name="separator"></param>
        private void AddColumns(SqlSelectStatement statement, Symbol symbol, ICollection<Symbol> columns,
                                IDictionary<string, Symbol> map, ref string separator)
        {
            JoinSymbol joinSymbol = symbol as JoinSymbol;
            if (null != joinSymbol)
            {
                if (!joinSymbol.IsNestedJoin)
                {
                    // Recurse if the join symbol is a collection of flattened extents
                    foreach (Symbol extent in joinSymbol.ExtentList)
                    {
                        // If extent is ScalarType means we are at base case in the
                        // recursion and there are not columns to add, just skip
                        if (MetadataHelpers.IsPrimitiveType(extent.Type))
                        {
                            continue;
                        }

                        this.AddColumns(statement, extent, columns, map, ref separator);
                    }
                }
                else
                {
                    foreach (Symbol joinColumn in joinSymbol.ColumnList)
                    {
                        // We write tableName.columnName
                        // rather than tableName.colmnName as alias
                        // since the column name is unique (by the way we generate new column names)
                        //
                        // We use the symbols for both the table and the column,
                        // since they are subject to renaming.
                        statement.Select.Append(separator);
                        statement.Select.Append(symbol);
                        statement.Select.Append(".");
                        statement.Select.Append(joinColumn);

                        // Check for name collisions. If there is,
                        // flag both the colliding symbols.
                        if (map.ContainsKey(joinColumn.Name))
                        {
                            map[joinColumn.Name].NeedsRenaming = true; // the original symbol
                            joinColumn.NeedsRenaming = true; // the current symbol
                        }
                        else
                        {
                            map[joinColumn.Name] = joinColumn;
                        }

                        columns.Add(joinColumn);
                        separator = ", ";
                    }
                }
            }
            else
            {
                // This is a non-join extent/select statement, and the CQT type has
                // the relevant column information.

                // The type could be a record type (e.g. Project(...),
                // or an entity type ( e.g. EntityExpression(...)
                // so, we check whether it is a structuralType.

                // Consider an expression of the form J(a, b=P(E))
                // The inner P(E) would have been translated to a SQL statement
                // We should not use the raw names from the type, but the equivalent
                // symbols (thet are present in symbol.Columns) if they exist.

                // We add the new columns to the symbol's columns if they do
                // not already exist.

                foreach (EdmProperty property in MetadataHelpers.GetProperties(symbol.Type))
                {
                    string propertyName = property.Name;

                    // Since all renaming happens in the second phase
                    // we lose nothing by setting the next column name index to 0
                    // many times
                    this.AllColumnNames[propertyName] = 0;

                    // Create a new symbol/reuse existing symbol for the column
                    Symbol columnSymbol;
                    if (!symbol.Columns.TryGetValue(propertyName, out columnSymbol))
                    {
                        // we dot not care about the types of columns, so we pass null
                        // when construction the symbol.
                        columnSymbol = new Symbol(propertyName, null);
                        symbol.Columns.Add(propertyName, columnSymbol);
                    }

                    statement.Select.Append(separator);
                    statement.Select.Append(symbol);
                    statement.Select.Append(".");

                    // we use the actual name before the "AS", the new name goes
                    // after the AS
                    statement.Select.Append(this.QuoteIdentifier(propertyName));

                    statement.Select.Append(" AS ");
                    statement.Select.Append(columnSymbol);

                    // check for column name collisions.
                    if (map.ContainsKey(propertyName))
                    {
                        map[propertyName].NeedsRenaming = true;
                        columnSymbol.NeedsRenaming = true;
                    }
                    else
                    {
                        map[propertyName] = symbol.Columns[propertyName];
                    }

                    columns.Add(columnSymbol);
                    separator = ", ";
                }
            }
        }

        /// <summary>
        /// Expands Select * to "select the_list_of_columns"
        /// If the columns are taken from an extent, they are written as
        /// {original_column_name AS Symbol(original_column)} to allow renaming.
        /// </summary>
        /// <param name="statement"></param>
        /// <returns></returns>
        private List<Symbol> AddDefaultColumns(SqlSelectStatement statement)
        {
            // This is the list of columns added in this select statement
            // This forms the "type" of the Select statement, if it has to
            // be expanded in another SELECT *
            List<Symbol> columns = new List<Symbol>();

            // A lookup for the previous set of columns to aid column name
            // collision detection.
            Dictionary<string, Symbol> map = new Dictionary<string, Symbol>(StringComparer.OrdinalIgnoreCase);

            string separator = string.Empty;

            // The Select should usually be empty before we are called,
            // but we do not mind if it is not.
            if (!statement.Select.IsEmpty)
            {
                separator = ", ";
            }

            statement.FromExtents.ForEach(
                symbol => this.AddColumns(statement, symbol, columns, map, ref separator));
            return columns;
        }

        /// <summary>
        /// If the sql fragment for an input expression is not a OracleSelect statement
        /// or other acceptable from (e.g. an extent as a OracleBuilder), we need
        /// to wrap it in a form acceptable in a FROM clause. These are
        /// primarily the
        /// <list type="bullet">
        /// <item>The set operaton expressions - union all, intersect, except</item>
        /// <item>TVFs, which are conceptually similar to tables</item>
        /// </list>
        /// </summary>
        /// <param name="statement"></param>
        /// <param name="fragment"></param>
        /// <param name="expressionKind"></param>
        private static void WrapNonQueryExtent(SqlSelectStatement statement, ISqlFragment fragment,
                                               DbExpressionKind expressionKind)
        {
            switch (expressionKind)
            {
                case DbExpressionKind.Function:
                    statement.From.Append(fragment);
                    break;

                default:
                    statement.From.Append(" (");
                    statement.From.Append(fragment);
                    statement.From.Append(")");
                    break;
            }
        }

        /// <summary>
        /// This is called by the relational nodes. It does the following
        /// <list type="bullet">
        /// <item>If the input is not a OracleSelectStatement, it assumes that the input
        /// is a collection expression, and creates a new OracleSelectStatement.
        /// </item>
        /// </list>
        /// </summary>
        /// <param name="inputExpression"></param>
        /// <param name="parameterName"></param>
        /// <param name="usage"></param>
        /// <param name="symbol"></param>
        /// <returns></returns>
        private SqlSelectStatement VisitInputExpression(DbExpression inputExpression, string parameterName,
                                                        TypeUsage usage, out Symbol symbol)
        {
            ISqlFragment fragment = inputExpression.Accept(this);

            SqlSelectStatement statement = fragment as SqlSelectStatement;
            if (null == statement)
            {
                statement = new SqlSelectStatement();
                WrapNonQueryExtent(statement, fragment, inputExpression.ExpressionKind);
            }

            if (0 == statement.FromExtents.Count)
            {
                // input was an extent
                symbol = new Symbol(parameterName, usage);
            }
            else if (1 == statement.FromExtents.Count)
            {
                // input was Filter/GroupBy/Project/OrderBy
                // we are likely to reuse this statement.
                symbol = statement.FromExtents[0];
            }
            else
            {
                // input was a join.
                // we are reusing the select statement produced by a Join node
                // we need to remover the original extents, and replace them with a
                // new extent with just the Join symbol.
                JoinSymbol joinSymbol = new JoinSymbol(parameterName, usage, statement.FromExtents)
                                     {
                                         FlattenedExtentList = statement.AllJoinExtents
                                     };

                symbol = joinSymbol;
                statement.FromExtents.Clear();
                statement.FromExtents.Add(symbol);
            }

            return statement;
        }

        /// <summary>
        /// </summary>
        /// <param name="binding"></param>
        /// <param name="predicate"></param>
        /// <param name="negatePredicate"></param>
        /// <returns></returns>
        private SqlSelectStatement VisitFilterExpression(DbExpressionBinding binding, DbExpression predicate,
                                                         bool negatePredicate)
        {
            Symbol symbol;
            SqlSelectStatement statement = this.VisitInputExpression(binding.Expression, binding.VariableName,
                                                                     binding.VariableType, out symbol);

            // filter is compatible with OrderBy
            // but not with Project, another Filter or GroupBy
            if (!IsCompatible(statement, DbExpressionKind.Filter))
            {
                statement = this.CreateNewSelectStatement(statement, binding.VariableName, binding.VariableType,
                                                          out symbol);
            }

            this._statements.Push(statement);
            this._symbols.EnterScope();

            this.AddFromSymbol(statement, binding.VariableName, symbol);

            if (negatePredicate)
            {
                statement.Where.Append("NOT (");
            }

            statement.Where.Append(predicate.Accept(this));
            if (negatePredicate)
            {
                statement.Where.Append(")");
            }

            this._symbols.ExitScope();
            this._statements.Pop();

            return statement;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <param name="negate"></param>
        /// <returns></returns>
        private SqlBuilder VisitIsEmptyExpression(DbIsEmptyExpression expression, bool negate)
        {
            SqlBuilder builder = new SqlBuilder();
            if (!negate)
            {
                builder.Append(" NOT");
            }

            builder.Append(" EXISTS (");
            builder.Append(this.VisitExpressionEnsureSqlStatement(expression.Argument));
            builder.AppendLine();
            builder.Append(")");

            return builder;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <param name="negate"></param>
        /// <returns></returns>
        private SqlBuilder VisitIsNullExpression(DbIsNullExpression expression, bool negate)
        {
            SqlBuilder builder = new SqlBuilder();
            builder.Append(expression.Argument.Accept(this));
            if (!negate)
            {
                builder.Append(" IS NULL");
            }
            else
            {
                builder.Append(" IS NOT NULL");
            }

            return builder;
        }

        /// <summary>
        /// This is used to determine if a calling expression needs to place
        /// round brackets around the translation of the expression.
        /// <see cref="expression" />
        /// </summary>
        /// <remarks>
        /// Constants, parameters and properties do not require brackets,
        /// everything else does.
        /// </remarks>
        /// <param name="expression"></param>
        /// <returns>true, if the expression needs brackets; false, otherwise</returns>
        private static bool IsComplexExpression(DbExpression expression)
        {
            switch (expression.ExpressionKind)
            {
                case DbExpressionKind.Constant:
                case DbExpressionKind.ParameterReference:
                case DbExpressionKind.Property:
                    return false;

                default:
                    return true;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="aggregates"></param>
        /// <returns></returns>
        private static bool NeedsInnerQuery(IEnumerable<DbAggregate> aggregates)
        {
            foreach (DbAggregate aggregate in aggregates)
            {
                Contract.Assert(1 == aggregate.Arguments.Count);
                if (!IsPropertyOverVarRef(aggregate.Arguments[0]))
                {
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        private static bool IsPropertyOverVarRef(DbExpression expression)
        {
            DbPropertyExpression propertyExpression = expression as DbPropertyExpression;
            if (null == propertyExpression)
            {
                return false;
            }

            DbVariableReferenceExpression varRefExpression = propertyExpression.Instance as DbVariableReferenceExpression;
            return null != varRefExpression;
        }

        /// <summary>
        /// Determine if the owner expression can add its unique sql to the input's
        /// SqlSelectStatement.
        /// </summary>
        /// <param name="statement">The SqlSelecteStatement of the input to the relational node.</param>
        /// <param name="expressionKind">The kind of the expression node (not the iunput's).</param>
        /// <returns></returns>
        protected static bool IsCompatible(SqlSelectStatement statement, DbExpressionKind expressionKind)
        {
            switch (expressionKind)
            {
                case DbExpressionKind.Skip:
                    return statement.Select.IsEmpty && statement.GroupBy.IsEmpty && statement.OrderBy.IsEmpty &&
                           !statement.IsDistinct;

                case DbExpressionKind.Sort:
                    return statement.Select.IsEmpty && statement.GroupBy.IsEmpty && statement.OrderBy.IsEmpty;

                case DbExpressionKind.Project:
                    return statement.Select.IsEmpty && statement.GroupBy.IsEmpty;

                case DbExpressionKind.Limit:
                    return false;

                case DbExpressionKind.Element:
                    return true;

                case DbExpressionKind.Distinct:
                    return (null == SqlSelectStatement.Top) && statement.OrderBy.IsEmpty;

                case DbExpressionKind.Filter:
                    return statement.Select.IsEmpty && statement.Where.IsEmpty && statement.GroupBy.IsEmpty &&
                           (null == SqlSelectStatement.Top);

                case DbExpressionKind.GroupBy:
                    return statement.Select.IsEmpty && statement.GroupBy.IsEmpty && statement.OrderBy.IsEmpty &&
                           (null == SqlSelectStatement.Top);

                default:
                    throw new NotSupportedException();
            }
        }

        /// <summary>
        /// This handles the processing of join expressions.
        /// The extents on a left spine are flattened, while joins
        /// not on the left spine give rise to new nested sub queries.
        /// </summary>
        /// <param name="bindings"></param>
        /// <param name="joinKind"></param>
        /// <param name="joinString"></param>
        /// <param name="joinCondition"></param>
        /// <returns></returns>
        /// <remarks>
        /// Joins works differently from the rest of the visiting, in that
        /// the parent (i.e. the join node) create the <see cref="SqlSelectStatement" />
        /// for the children to use.
        /// 
        /// The "parameter" IsInJoinContext indicates whether a child extent should add
        /// its stuff to the existing OracleSelectStatement, or create a new OracleSelectStatement.
        /// By passing true, we ask the children to add themselves to the parent join,
        /// by passing false, we ask the children to create new Select statements for
        /// themselves.
        /// </remarks>
        private ISqlFragment VisitJoinExpression(IList<DbExpressionBinding> bindings, DbExpressionKind joinKind,
                                                 string joinString, DbExpression joinCondition)
        {
            SqlSelectStatement statement;

            // If the parent is not a join (or says that it is not),
            // we should create a new OracleSelectStatement.
            // otherwise, we add our child extents to the parent's FROM clause.
            if (!this.IsParentAJoin)
            {
                statement = new SqlSelectStatement { AllJoinExtents = new List<Symbol>() };
                this._statements.Push(statement);
            }
            else
            {
                statement = this.CurrentStatement;
            }

            // Process each of the inputs, and the the JoinCondition if it exists.
            // It would be nice if we could call VisitInputExpression - that would
            // avoid some code duplication, but the Join postprocessing is messy
            // and prevents this reuse.
            this._symbols.EnterScope();

            string separator = string.Empty;
            bool isLeftMostInput = true;

            for (int index = 0, max = bindings.Count; index < max; index++)
            {
                DbExpressionBinding binding = bindings[index];
                if (!string.IsNullOrEmpty(separator))
                {
                    statement.From.AppendLine();
                }

                statement.From.Append(separator + " ");

                // Change this if other conditions are required
                // to force the child to produce a nested OracleStatement.
                bool needsJoinContext = (DbExpressionKind.Scan == binding.Expression.ExpressionKind) ||
                                        (isLeftMostInput &&
                                         (IsJoinExpression(binding.Expression) || IsApplyExpression(binding.Expression)));

                this._joins.Push(needsJoinContext);

                // If the child reuses our select statement, it will append the from symbol
                // to our FromExtents list. So, we need to remember the start of the child's entries.
                int count = statement.FromExtents.Count;

                ISqlFragment fragment = binding.Expression.Accept(this);

                this._joins.Pop();

                this.ProcessJoinInputResult(fragment, statement, binding, count);
                separator = joinString;
            }

            // Visit the on clause/join condition.
            switch (joinKind)
            {
                case DbExpressionKind.FullOuterJoin:
                case DbExpressionKind.InnerJoin:
                case DbExpressionKind.LeftOuterJoin:
                    statement.From.Append(" ON ");
                    this._joins.Push(false);
                    statement.From.Append(joinCondition.Accept(this));
                    this._joins.Pop();
                    break;
            }

            this._symbols.ExitScope();

            if (!this.IsParentAJoin)
            {
                this._statements.Pop();
            }

            return statement;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="fragment"></param>
        /// <param name="statement"></param>
        /// <param name="binding"></param>
        /// <param name="index"></param>
        private void ProcessJoinInputResult(ISqlFragment fragment, SqlSelectStatement statement,
                                            DbExpressionBinding binding, int index)
        {
            Symbol symbol = null;

            if (fragment != statement)
            {
                // The child has its own select statement, and is not reusing
                // our select statement.
                // This should look a lot like VisitInputExpression()
                SqlSelectStatement query = fragment as SqlSelectStatement;
                if (null != query)
                {
                    if (query.Select.IsEmpty)
                    {
                        List<Symbol> columns = this.AddDefaultColumns(query);
                        if (IsJoinExpression(binding.Expression) || IsApplyExpression(binding.Expression))
                        {
                            List<Symbol> extents = query.FromExtents;
                            JoinSymbol joinSymbol =
                                new JoinSymbol(binding.VariableName, binding.VariableType, extents)
                                    {
                                        IsNestedJoin = true,
                                        ColumnList = columns
                                    };

                            symbol = joinSymbol;
                        }
                        else
                        {
                            // This is a copy of the code in CreateNewSelectStatement

                            // if the oldStatement has a join as its input,
                            // clone the join symbol, so that we "reuse" the
                            // join symbol. Normally, we create a new symbol
                            JoinSymbol joinSymbol = query.FromExtents[0] as JoinSymbol;
                            if (null != joinSymbol)
                            {
                                // Note : query.FromExtents will not do, since it might
                                // just be an alias of joinSymbol, and we xant an actual JoinSymbol.
                                JoinSymbol newer = new JoinSymbol(binding.VariableName, binding.VariableType,
                                                           joinSymbol.ExtentList)
                                                {
                                                    // This indicates that the query is a blocking scope
                                                    // i.e. it hides/renabme extent columns.
                                                    IsNestedJoin = true,
                                                    ColumnList = columns,
                                                    FlattenedExtentList = joinSymbol.FlattenedExtentList
                                                };

                                symbol = newer;
                            }
                        }
                    }

                    statement.From.Append(" (");
                    statement.From.Append(query);
                    statement.From.Append(" )");
                }
                else if (binding.Expression is DbScanExpression)
                {
                    statement.From.Append(fragment);
                }
                else
                {
                    WrapNonQueryExtent(statement, fragment, binding.Expression.ExpressionKind);
                }

                if (null == symbol)
                {
                    symbol = new Symbol(binding.VariableName, binding.VariableType);
                }

                this.AddFromSymbol(statement, binding.VariableName, symbol);
                statement.AllJoinExtents.Add(symbol);
            }
            else
            {
                // we are adding extents to the current sql statement via flattening.
                // we are replacing the child's extents with a single Join symbol.
                // the child's extents are all those following the index index.
                List<Symbol> extents = new List<Symbol>();

                // we cannot call extents.AddRange, since the is no simply way to
                // get the range of symbols index..builder.FromExtents.Count
                // from statement.FromExtents.
                // we copy these symbols to create the JoinSymbol later.
                for (int dummy = index, max = statement.FromExtents.Count; dummy < max; ++dummy)
                {
                    extents.Add(statement.FromExtents[dummy]);
                }

                statement.FromExtents.RemoveRange(index, statement.FromExtents.Count - index);
                symbol = new JoinSymbol(binding.VariableName, binding.VariableType, extents);
                statement.FromExtents.Add(symbol);

                // this JoinSymbol does not have its own select statement, so we
                // do not set IsNestedJoin.

                // we do not call AddFromSymbol(), since we do not want to add
                // "AS alias" to the FROM clause.
                // it has been done when the extent was added earlier.
                this._symbols.Add(binding.VariableName, symbol);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        private ISqlFragment HandleCountExpression(DbExpression expression)
        {
            if (DbExpressionKind.Constant == expression.ExpressionKind)
            {
                // for constant expression we should not cast the value,
                // thus we don't go throught the default DbConstantExpression handling
                SqlBuilder builder = new SqlBuilder();
                builder.Append(((DbConstantExpression) expression).Value.ToString());
                return builder;
            }

            return expression.Accept(this);
        }

        /// <summary>
        /// Handler for special canonical functions.
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        private ISqlFragment HandleCanonicalFunctions(DbFunctionExpression expression)
        {
            return this.DispatchProcessing(_functors, expression);
        }

        /// <summary>
        /// Default handling for functions.
        /// Translates them to FunctionName(arg1, arg2, ..., argN)
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        private ISqlFragment HandleDefaultFunctions(DbFunctionExpression expression)
        {
            SqlBuilder builder = new SqlBuilder();
            this.WriteFunctionName(builder, expression.Function);
            this.HandleFunctionArguments(builder, expression);
            return builder;
        }

        /// <summary>
        /// Writes the function name to the given SqlBuilder.
        /// </summary>
        /// <param name="builder"></param>
        /// <param name="function"></param>
        private void WriteFunctionName(SqlBuilder builder, EdmFunction function)
        {
            string name = MetadataHelpers.GetMetadataProperty<string>(function, MetadataHelpers.StoreFunctionNameMetadata);

            string storeFunctionName = name ?? function.Name;

            // If the function is a builtin (i.e. the BuiltIn attribute has been
            // specified, both store and canonical functions have this attribute), 
            // then the function name should not be quoted;
            // additionally, no namespace should be used.
            if (MetadataHelpers.IsCanonicalFunction(function))
            {
                builder.Append(storeFunctionName.ToUpperInvariant());
            }
            else if (IsBuiltInStoreFunction(function))
            {
                builder.Append(storeFunctionName);
            }
            else
            {
                string schema = MetadataHelpers.GetMetadataProperty<string>(function, MetadataHelpers.SchemaMetadata);

                // Should we actually support this ?
                if (string.IsNullOrEmpty(schema))
                {
                    builder.Append(this.QuoteIdentifier(function.NamespaceName));
                }
                else
                {
                    builder.Append(this.QuoteIdentifier(schema));
                }

                builder.Append(".");
                builder.Append(this.QuoteIdentifier(storeFunctionName));
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="builder"></param>
        /// <param name="expression"></param>
        private void HandleFunctionArguments(SqlBuilder builder, DbFunctionExpression expression)
        {
            bool isNiladicFunction = MetadataHelpers.GetMetadataProperty<bool>(expression.Function, MetadataHelpers.NiladicFunctionMetadata);

            Contract.Assert(!(isNiladicFunction && (0 < expression.Arguments.Count)), "function attributed as NiladicFunction='true' in the provicer manifest cannot have arguments.");
            if (isNiladicFunction && 0 < expression.Arguments.Count)
            {
                throw new MetadataException("Functions listed in the provider manifest that are attributed as NiladicFunction='true' cannot have arguments.");
            }

            if (!isNiladicFunction)
            {
                builder.Append("(");

                string separator = string.Empty;
                foreach (DbExpression argument in expression.Arguments)
                {
                    builder.Append(separator);
                    builder.Append(argument.Accept(this));
                    separator = ", ";
                }

                builder.Append(")");
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <param name="builder"></param>
        private void ParenthesizeExpression(DbExpression expression, SqlBuilder builder)
        {
            if (IsComplexExpression(expression))
            {
                builder.Append("(");
            }

            builder.Append(expression.Accept(this));

            if (IsComplexExpression(expression))
            {
                builder.Append(")");
            }
        }

        /// <summary>
        /// Is this a Store function 
        /// </summary>
        /// <param name="function"></param>
        /// <returns></returns>
        private static bool IsBuiltInStoreFunction(EdmFunction function)
        {
            bool attribute = MetadataHelpers.GetMetadataProperty<bool>(function, MetadataHelpers.BuiltInMetadata);
            return attribute && !MetadataHelpers.IsCanonicalFunction(function);
        }

        /// <summary>
        /// Disptacth the special function processing to the appropriate handler.
        /// </summary>
        /// <param name="functors"></param>
        /// <param name="expression"></param>
        /// <returns></returns>
        private ISqlFragment DispatchProcessing(IDictionary<string, CanonicalFunctor> functors, DbFunctionExpression expression)
        {
            Contract.Assert(functors.ContainsKey(expression.Function.Name), "Special handling should be called only for functions in the list of special functions.");
            return functors[expression.Function.Name](this, expression);
        }

        /// <summary>
        /// This is used to determine if a particular expression is an Apply operation.
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        private static bool IsApplyExpression(DbExpression expression)
        {
            return ((DbExpressionKind.CrossApply == expression.ExpressionKind) || (DbExpressionKind.OuterApply == expression.ExpressionKind));
        }

        /// <summary>
        /// This is used to determine if a particular expression is a Join operation.
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        private static bool IsJoinExpression(DbExpression expression)
        {
            return DbExpressionKind.CrossJoin == expression.ExpressionKind ||
                   DbExpressionKind.FullOuterJoin == expression.ExpressionKind ||
                   DbExpressionKind.InnerJoin == expression.ExpressionKind ||
                   DbExpressionKind.LeftOuterJoin == expression.ExpressionKind;
        }

        /// <summary>
        /// Determines whether the given function is a canonical function that
        /// requires special handling.
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        private static bool IsSpecialCanonicalFunction(DbFunctionExpression expression)
        {
            return MetadataHelpers.IsCanonicalFunction(expression.Function) && _functors.ContainsKey(expression.Function.Name);
        }

        #endregion
    }
}